#include "Game.hpp"
#include "config.hpp"
#include <algorithm>
#include <array>
#include <opencv2/core/mat.hpp>
#include <opencv2/core/types.hpp>

Game::Game() {
  // 使用引用遍历确保正确初始化每个滤波器
  for (auto &filter : filter_) {
    filter = std::make_shared<CircularBufferFilter>();
  }
}
Game::~Game() {}
cv::Point2f init_thresholds(cv::Mat &black_roi, cv::Mat &white_roi) {
  cv::Scalar black_mean = cv::mean(black_roi);
  float black_thresh =
      static_cast<float>((black_mean[0] + black_mean[1] + black_mean[2]) / 3.0);

  cv::Scalar white_mean = cv::mean(white_roi);
  float white_thresh =
      static_cast<float>((white_mean[0] + white_mean[1] + white_mean[2]) / 3.0);

  return cv::Point2f(black_thresh, white_thresh);
}

std::array<chessPiece, 9> Game::processImage(const cv::Mat &input) {
  // 预处理图像
  cv::Mat preprocessed = preprocessImage(input);

  // 找到棋盘四个角点
  std::vector<cv::Point2f> corners = findBoardCorners(preprocessed);

  if (corners.size() != 4) {
    std::cout << "未能找到棋盘的四个角点" << std::endl;
    // std::cout<<"1"<<std::endl;
    return std::array<chessPiece, 9>{chessPiece::NONE};
  } else {
    std::cout << "找到棋盘的四个角点" << std::endl;
    // std::cout<<"2"<<std::endl;
  }

  // 进行透视变换
  cv::Mat transformed = perspectiveTransform(input, corners);
  cv::imshow("trans", transformed);

  // 分割棋盘
  std::vector<cv::Mat> cells = splitBoard(transformed);

  // 识别每个格子的棋子类型
  std::array<chessPiece, 9> board;
  for (int i = 0; i < 9; ++i) {
    board[i] = determineChessPiece(cells[i]);
  }

  return board;
}

cv::Mat Game::preprocessImage(const cv::Mat &input) {
  cv::Mat blurred, gray, binary;

  cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);

  // 高斯模糊去噪
  cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);

  // 使用自适应二值化替代Canny边缘检测
  cv::adaptiveThreshold(blurred, binary, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C,
                        cv::THRESH_BINARY_INV,
                        11, // 邻域大小（必须是奇数）
                        2); // 常数C，控制阈值偏移

  // 1. 先腐蚀再膨胀（开运算）
  cv::Mat eroded;
  cv::erode(binary, eroded,
            cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));

  cv::Mat opened;
  cv::dilate(eroded, opened,
             cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 5)));

  if (DEBUG) {
    cv::imshow("Opened", opened);
  }

  // 2. 提取轮廓
  std::vector<std::vector<cv::Point>> contours;
  cv::findContours(opened.clone(), contours, cv::RETR_EXTERNAL,
                   cv::CHAIN_APPROX_SIMPLE);

  // 3. 过滤掉面积小于阈值的轮廓
  cv::Mat result = cv::Mat::zeros(opened.size(), CV_8UC1);
  for (const auto &contour : contours) {
    double area = cv::contourArea(contour);
    if (area >= 2000) {
      cv::drawContours(result, std::vector<std::vector<cv::Point>>{contour}, -1,
                       cv::Scalar(255), -1);
    }
  }

  if (DEBUG) {
    cv::imshow("Filtered Contours", result);
  }

  return result;
}

std::vector<cv::Point2f> Game::findBoardCorners(const cv::Mat &binary) {
  std::vector<std::vector<cv::Point>> contours;
  cv::findContours(binary, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

  std::vector<cv::Point2f> bestCorners;
  double maxArea = 0;
  double minAspectRatioDiff = std::numeric_limits<double>::max();

  // 遍历所有轮廓
  for (const auto &contour : contours) {
    // 计算轮廓面积
    double area = cv::contourArea(contour);
    // if (area < 100000)
    //   continue; // 忽略太小的轮廓
    // if (area > 131780) {
    //   continue; // 忽略太大的轮廓
    // }
    // 多边形逼近
    std::vector<cv::Point> approx;
    double epsilon = 0.02 * cv::arcLength(contour, true);
    cv::approxPolyDP(contour, approx, epsilon, true);

    // 只考虑四边形
    if (approx.size() != 4)
      continue;

    // 转换为Point2f
    std::vector<cv::Point2f> corners(approx.begin(), approx.end());

    // 计算长宽比
    cv::Point2f edges[4];
    edges[0] = corners[1] - corners[0];
    edges[1] = corners[2] - corners[1];
    edges[2] = corners[3] - corners[2];
    edges[3] = corners[0] - corners[3];

    double lengths[4];
    for (int i = 0; i < 4; i++) {
      lengths[i] = std::sqrt(edges[i].x * edges[i].x + edges[i].y * edges[i].y);
    }

    // 计算对边长度比
    double ratio1 = lengths[0] / lengths[2];
    double ratio2 = lengths[1] / lengths[3];

    // 理想情况下，对边长度比应该接近1
    double aspectRatioDiff = std::abs(1 - ratio1) + std::abs(1 - ratio2);
    // 如果这是目前发现的最好的候选者
    if (area > maxArea && aspectRatioDiff < 0.5) {
      maxArea = area;
      bestCorners = corners;
    }
  }

  if (bestCorners.empty()) {
    // std::cerr << "无法找到合适的棋盘轮廓" << std::endl;
    return std::vector<cv::Point2f>{cv::Point2f(0, 0)};
  }

  // 对角点进行排序，确保顺序是：左上、右上、右下、左下
  std::vector<cv::Point2f> &corners = bestCorners;
  // std::cout<<"找到棋盘角点: "<<std::endl;
  // 计算重心
  cv::Point2f center(0, 0);
  for (const auto &corner : corners) {
    center += corner;
  }
  center *= (1.0 / 4);

  // 根据点到重心的角度排序
  std::sort(corners.begin(), corners.end(),
            [center](const cv::Point2f &p1, const cv::Point2f &p2) {
              double angle1 = std::atan2(p1.y - center.y, p1.x - center.x);
              double angle2 = std::atan2(p2.y - center.y, p2.x - center.x);
              return angle1 < angle2;
            });

  // 旋转数组使得左上角点为第一个点
  int topLeftIdx = 0;
  double minSum = std::numeric_limits<double>::max();
  for (int i = 0; i < 4; i++) {
    double sum = corners[i].x + corners[i].y;
    if (sum < minSum) {
      minSum = sum;
      topLeftIdx = i;
    }
  }

  std::rotate(corners.begin(), corners.begin() + topLeftIdx, corners.end());

  if (DEBUG) {
    cv::Mat debug;
    cv::cvtColor(binary, debug, cv::COLOR_GRAY2BGR);
    for (int i = 0; i < 4; i++) {
      cv::circle(debug, corners[i], 5, cv::Scalar(0, 0, 255), -1);
      cv::line(debug, corners[i], corners[(i + 1) % 4], cv::Scalar(0, 255, 0),
               2);
    }
    cv::imshow("角点检测", debug);
  }

  std::array<double, 4> weights = {1.0, 1.0, 1.0, 1.0};
  for (int i = 0; i < 4; i++) {
    cv::Mat roi;
    roi = binary(cv::Rect(static_cast<int>(corners[i].x - 2),
                          static_cast<int>(corners[i].y - 2), 4, 4));
    cv::Scalar mean = cv::mean(roi);
    float avgIntensity =
        static_cast<float>((mean[0] + mean[1] + mean[2]) / 3.0);
    weights[i] = avgIntensity;
    filter_[i]->addData(corners[i], weights[i]);

  }

  // 返回滤波结果
  std::vector<cv::Point2f> filteredCorners;

  filteredCorners.reserve(4);
  for (int i = 0; i < 4; i++) {
    filteredCorners.push_back(filter_[i]->getFilteredPoint());
  }

  return filteredCorners;
}

cv::Mat Game::perspectiveTransform(const cv::Mat &input,
                                   const std::vector<cv::Point2f> &corners) {
  // 定义目标角点
  std::vector<cv::Point2f> dst_corners = {
      cv::Point2f(0, 0), cv::Point2f(BOARD_SIZE - 1, 0),
      cv::Point2f(BOARD_SIZE - 1, BOARD_SIZE - 1),
      cv::Point2f(0, BOARD_SIZE - 1)};

  // 计算透视变换矩阵
  cv::Mat transform = cv::getPerspectiveTransform(corners, dst_corners);

  // 进行透视变换
  cv::Mat output;
  cv::warpPerspective(input, output, transform,
                      cv::Size(BOARD_SIZE, BOARD_SIZE));

  return output;
}

std::vector<cv::Mat> Game::splitBoard(const cv::Mat &board) {
  std::vector<cv::Mat> cells;
  int cellSize = BOARD_SIZE / 3;

  // 从上到下，从左到右分割棋盘
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      cv::Rect roi(j * cellSize, i * cellSize, cellSize, cellSize);
      cells.push_back(board(roi).clone());
    }
  }

  return cells;
}

chessPiece Game::determineChessPiece(const cv::Mat &cell) {
  cv::Scalar mean = cv::mean(cell);
  double avgIntensity = (mean[0] + mean[1] + mean[2]) / 3.0;

  // 使用配置文件中的阈值判断棋子类型
  if (avgIntensity < findboxConfig::MIN_COLOR_THER) {
    std::cout << "检测到黑子，平均强度: " << avgIntensity << std::endl;
    return chessPiece::BLACK;
  } else if (avgIntensity > findboxConfig::MAX_COLOR_THER) {
    std::cout << "检测到白子，平均强度: " << avgIntensity << std::endl;
    return chessPiece::WHITE;
  }

  std::cout << "检测到空格，平均强度: " << avgIntensity << std::endl;
  return chessPiece::NONE; // 默认返回空格
}
